﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;

namespace FiveInARow.Components
{
	public class BoardPanel : Panel
	{
		#region .ctors

		public BoardPanel()
		{
			RenderTransformOrigin = new Point(0.5, 0.5);
			RenderTransform = _tTranslate = new TranslateTransform();

			Loaded += new RoutedEventHandler(BoardPanel_Loaded);
		}

		#endregion .ctors

		#region Fields

		private bool _isLeftMouseButtonDown;
		private Point? _lastLeftMouseButtonDownPosition, _lastMousePosition;
		private bool _isDragging;
		private TranslateTransform _tTranslate;

		#endregion Fields

		#region Properties

		public Func<object, Tuple<int, int>> IndexGetter
		{
			get { return (Func<object, Tuple<int, int>>)GetValue(IndexGetterProperty); }
			set { SetValue(IndexGetterProperty, value); }
		}

		// Using a DependencyProperty as the backing store for IndexGetter.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty IndexGetterProperty =
			DependencyProperty.Register("IndexGetter", typeof(Func<object, Tuple<int, int>>), typeof(BoardPanel), new UIPropertyMetadata(null));

		public Tuple<double, double> Offset
		{
			get { return (Tuple<double, double>)GetValue(OffsetProperty); }
			set { SetValue(OffsetProperty, value); }
		}

		// Using a DependencyProperty as the backing store for Offset.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty OffsetProperty =
			DependencyProperty.Register("Offset", typeof(Tuple<double, double>), typeof(BoardPanel), new FrameworkPropertyMetadata(new Tuple<double, double>(0, 0), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.AffectsArrange));

		#endregion Properties

		#region Methods

		void BoardPanel_Loaded(object sender, RoutedEventArgs e)
		{
			var owner = ItemsControl.GetItemsOwner(this);
			if (owner != null)
			{
				owner.MouseLeftButtonDown += new MouseButtonEventHandler(owner_MouseLeftButtonDown);
				owner.MouseLeftButtonUp += new MouseButtonEventHandler(owner_MouseLeftButtonUp);
				owner.MouseMove += new MouseEventHandler(owner_MouseMove);
			}
		}

		protected override System.Windows.Size MeasureOverride(System.Windows.Size availableSize)
		{
			if (InternalChildren.Count == 0 || IndexGetter == null || Offset == null)
			{
				return base.MeasureOverride(availableSize);
			}

			var items = InternalChildren.OfType<FrameworkElement>();
			foreach (var item in items)
			{
				item.Measure(availableSize);
			}

			var maxIndex = new Tuple<int, int>(items.Max(i => IndexGetter(i.DataContext).Item1), items.Max(i => IndexGetter(i.DataContext).Item2));
			var maxSize = new Size(items.Max(i => i.DesiredSize.Width), items.Max(i => i.DesiredSize.Height));

			return new Size(maxSize.Width * maxIndex.Item1, maxSize.Height * maxIndex.Item2);
		}

		protected override Size ArrangeOverride(Size finalSize)
		{
			if (InternalChildren.Count == 0 || IndexGetter == null || Offset == null)
			{
				return base.ArrangeOverride(finalSize);
			}

			var items = InternalChildren.OfType<FrameworkElement>();
			var maxSize = new Size(items.Max(i => i.DesiredSize.Width), items.Max(i => i.DesiredSize.Height));

			foreach (var item in items)
			{
				var index = IndexGetter(item.DataContext);
				item.Arrange(new Rect(maxSize.Width * (index.Item1 - 1) + Offset.Item1, maxSize.Height * (index.Item2 - 1) + Offset.Item2, maxSize.Width, maxSize.Height));
			}

			return base.ArrangeOverride(finalSize);
		}

		private void owner_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
		{
			_isLeftMouseButtonDown = true;
			_lastLeftMouseButtonDownPosition = e.GetPosition(this);
		}

		private void owner_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
		{
			_isLeftMouseButtonDown = false;
			_lastLeftMouseButtonDownPosition = null;
			Application.Current.MainWindow.Cursor = Cursors.Arrow;
			ReleaseMouseCapture();

			if (_isDragging)
			{
				_isDragging = false;
			}
			else
			{
				var res = InputHitTest(e.GetPosition(this));
				if (res != null)
				{
					var t = (DependencyObject)res;
					while (t != null && !(t is Cell))
					{
						t = VisualTreeHelper.GetParent(t);
					}
					if (t != null)
					{
						(t as Cell).Set();
					}
				}
			}
		}

		private void owner_MouseMove(object sender, MouseEventArgs e)
		{
			if (_isLeftMouseButtonDown)
			{
				if (_isDragging)
				{
					if (e.GetPosition(this) != null)
					{
						var dOffset = new Tuple<double, double>(
							(e.GetPosition(this) - _lastMousePosition.Value).X,
							(e.GetPosition(this) - _lastMousePosition.Value).Y);
						Offset = new Tuple<double, double>(Offset.Item1 + dOffset.Item1, Offset.Item2 + dOffset.Item2);
					}
				}
				else
				{
					if ((e.GetPosition(this) - _lastLeftMouseButtonDownPosition.Value).Length > 6)
					{
						_isDragging = true;
						Application.Current.MainWindow.Cursor = Cursors.SizeAll;
						CaptureMouse();
					}
				}
			}

			_lastMousePosition = e.GetPosition(this);
		}

		#endregion Methods
	}
}
